<template>
  <div class="file-upload">
      <div class="file-upload-container" @click.prevent="triggerUpload" v-bind="$attrs">
        <slot v-if="fileStatus === 'loading'" name="loading">
          <button class="btn btn-primary" disabled>正在上传...</button>
        </slot>
        <slot v-else-if="fileStatus === 'success'" name="uploaded" :uploadedData = "uploadedData">
          <button class="btn btn-primary" >上传成功</button>
        </slot>
        <slot v-else name="default">
          <button class="btn btn-primary" >点击上传</button>
        </slot>
      </div>
      <input
        type="file"
        class="file-input d-none"
        ref="fileInput"
        @change="handleFileChange"
        />
  </div>
</template>

<script lang="ts">
import { defineComponent, PropType, ref, watch } from 'vue'
import axios from 'axios'
type UploadStatus = 'ready' | 'loading' | 'success' | 'error'
type CheckFunction = (file: File) => boolean
export default defineComponent({
  // 制作发送 post 请求地址的 action
  props: {
    action: {
      type: String,
      required: true
    },
    beforeUpload: {
      type: Function as PropType<CheckFunction>
    },
    uploaded: {
      type: Object
    }
  },
  inheritAttrs: false,
  emits: ['file-uploaded', 'file-uploaded-error'],
  setup(props, context) {
    // 创建 scoped slot 所需的传出的图片内容
    const uploadedData = ref(props.uploaded)
    // 设置响应式对象 fileStatus 来表示上传状态，使用 UploadStatus 泛型来规定它，默认值为 ready
    const fileStatus = ref<UploadStatus>(props.uploaded ? 'success' : 'ready')
    // 在 setup 中拿到 dom 节点：给他传一个 HTMLInputElement 的泛型
    const fileInput = ref<null|HTMLInputElement>(null)
    // 需要追踪 uploaded 值的更新
    watch(() => props.uploaded, (newValue) => {
      // 如果更新了的话，就把状态变为 success
      if (newValue) {
        fileStatus.value = 'success'
        uploadedData.value = newValue
      }
    })
    const triggerUpload = () => {
      // 如果这个 dom 节点已经有了，就点击它
      if (fileInput.value) {
        fileInput.value.click()
      }
    }
    const handleFileChange = (e: Event) => {
      // 为了在 currentTarget 上拿到 files 的属性
      const currentTarget = e.target as HTMLInputElement
      if (currentTarget.files) {
        // 然后拿到了文件，因为他的格式是 FileList ，将其转换一下格式
        const files = Array.from(currentTarget.files)
        // 在 loading 之前检查 用户上传的类型是是否合适
        if (props.beforeUpload) {
          // 在 beforeUpload 存在的情况下，将检查的结果赋值为 result，如果 result 为 FALSE，就直接返回
          const result = props.beforeUpload(files[0])
          if (!result) {
            return
          }
        }
        // 已经选择了文件，将状态改为 loading
        fileStatus.value = 'loading'
        // 然后新建 formData
        const formData = new FormData()
        // 添加数据：只能上传一个数据
        formData.append('file', files[0])
        axios.post(props.action, formData, {
          headers: {
            'Content-type': 'multipart/form-data'
          }
        }).then(resp => {
          console.log(resp.data)
          fileStatus.value = 'success'
          // 成功拿到图片后 将其值存到 uploadedData 中
          uploadedData.value = resp.data
          context.emit('file-uploaded', resp.data)
        }).catch((error) => {
          fileStatus.value = 'error'
          context.emit('file-uploaded-error', { error })
        }).finally(() => {
          if (fileInput.value) {
            fileInput.value.value = ''
          }
        })
      }
    }
    return {
      fileInput,
      triggerUpload,
      fileStatus,
      handleFileChange,
      uploadedData
    }
  }
})
</script>

<style>

</style>
